<!--
 * @Author: 张天昊
 * @Date: 2024-09-06 10:53:59
 * @LastEditTime: 2024-10-31 09:52:04
 * @LastEditors: 张天昊
 * @Description: 图片文件预览上传
 * @FilePath: /sentiment-dispose-app/src/components/FilePreviewer/FileList.vue
-->
<script>
import { defineComponent, computed, ref } from 'vue'
import { ImagePreview, Toast } from 'vant'
import { getFilenameByUrl, getExtByName } from '@/utils/helper'
import FilePreviewer from './FilePreviewer.vue'
import UploadTrigger from './UploadTrigger.vue'
import { FileTypes, BusiFileTypes } from './fileType'
import { chooseImageUpload } from './chooseImageInApp'
import SquareContainer from '../SquareContainer.vue'
import icDelete from '@/assets/img/ic-delete.png'
import icFileCommon from '@/assets/img/ic-file-common.png'
import icFileDoc from '@/assets/img/ic-file-doc.png'
import icFileExl from '@/assets/img/ic-file-exl.png'
import icFileMp3 from '@/assets/img/ic-file-mp3.png'
import icFileMp4 from '@/assets/img/ic-file-mp4.png'
import icFilePdf from '@/assets/img/ic-file-pdf.png'
import icFileText from '@/assets/img/ic-file-text.png'
import icFileZip from '@/assets/img/ic-file-zip.png'

const ext2icon = new Map([
  [FileTypes.文稿, icFileDoc],
  [FileTypes.表格, icFileExl],
  [FileTypes.音频, icFileMp3],
  [FileTypes.视频, icFileMp4],
  [FileTypes.文档, icFilePdf],
  [FileTypes.压缩包, icFileZip],
  [FileTypes.文本, icFileText]
])

function getIconByExt(ext) {
  for (const [exts, icon] of ext2icon) {
    if (exts.includes(ext)) {
      return icon
    }
  }
  return icFileCommon
}

export default defineComponent({
  inheritAttrs: false,
  components: {
    FilePreviewer,
    UploadTrigger,
    SquareContainer
  },
  props: {
    files: {
      type: Array,
      default: () => []
    },
    removable: Boolean,
    upload: Boolean,
    collapsed: Boolean,
    preview: Boolean,
    imageMax: {
      type: Number,
      default: 20
    },
    otherMax: {
      type: Number,
      default: 20
    }
  },
  setup(props, { emit, expose }) {
    const imagesInFiles = computed(() =>
      props.files
        .map((item, index) => ({ ...item, idx: index }))
        .filter((item) => isImage(item))
    )
    const otherFiles = computed(() =>
      props.files
        .map((item, index) => ({ ...item, idx: index }))
        .filter((item) => !isImage(item))
    )
    function isImage(file) {
      return (
        file.type === BusiFileTypes.图片 ||
        FileTypes.图片
          .split(',')
          .includes(getExtByName(file.original || file.url))
      )
    }
    function onDelete(index) {
      const newFiles = props.files
        .slice(0, index)
        .concat(props.files.slice(index + 1))
      emit('update:files', newFiles)
    }
    function handlePreviewImage(index) {
      ImagePreview({
        images: imagesInFiles.value.map((img) => img.url),
        startPosition: index,
        className: 'custom-image-previewer',
        closeable: true
      })
    }

    const filePreviewer = ref(null)
    function handlePreviewFile(file) {
      const ext = getExtByName(file.original || file.url)
      filePreviewer.value?.preview(ext, file.url)
    }
    async function onUpload() {
      const iLen = imagesInFiles.value.length
      const oLen = otherFiles.value.length
      if (iLen >= props.imageMax && oLen >= props.otherMax) {
        Toast('图片和其他类型文件上传数量已达上限')
        return
      }

      const res = await chooseImageUpload({
        maxImage: props.imageMax - imagesInFiles.value.length,
        maxFile: props.otherMax - otherFiles.value.length
      })
      if (res) {
        emit('update:files', props.files.concat(res))
      }
    }
    expose({
      handleUpload: onUpload
    })

    return {
      icDelete,
      imagesInFiles,
      otherFiles,
      filePreviewer,
      handlePreviewImage,
      handlePreviewFile,
      onDelete,
      onUpload,
      getFilenameByUrl,
      getIconByExt,
      getExtByName
    }
  }
})
</script>

<template>
  <div>
    <div class="w-full" v-bind="$attrs">
      <div v-if="imagesInFiles.length > 0" class="file-images-container">
        <SquareContainer
          v-for="(item, index) in collapsed
            ? imagesInFiles.slice(0, 4)
            : imagesInFiles"
          :key="item.url || item.original"
          @click="handlePreviewImage(index)"
        >
          <!-- 可删除 -->
          <template v-if="removable">
            <!-- 折叠 - 第四个显示数量 -->
            <div
              v-if="collapsed && imagesInFiles.length > 4 && index === 3"
              class="relative h-full w-full"
              @click="$emit('more')"
            >
              <div
                class="absolute bottom-0 left-0 right-0 top-0 z-1 flex items-center justify-center rounded-4 bg-black/60 text-20 text-white leading-28"
              >
                +{{ imagesInFiles.length - 3 }}
              </div>
              <van-image
                width="100%"
                height="100%"
                fit="cover"
                :src="item.url"
                :radius="4"
                round
              />
            </div>
            <!-- 不论是否折叠 - 图片+删除按钮 -->
            <div v-else class="relative h-full w-full">
              <van-icon
                class="z-20 -right-2.5 -top-2.5"
                :style="{ position: 'absolute' }"
                :size="20"
                :name="icDelete"
                @click.stop="onDelete(item.idx)"
              />
              <van-image
                width="100%"
                height="100%"
                fit="cover"
                :src="item.url"
                :radius="4"
                round
              />
            </div>
          </template>
          <!-- 不可删除 -->
          <template v-else>
            <!-- 折叠 - 第四个显示数量 -->
            <div
              v-if="collapsed && imagesInFiles.length > 4 && index === 3"
              class="relative h-full w-full"
              @click="$emit('more')"
            >
              <div>+{{ imagesInFiles.length - 3 }}</div>
              <van-image
                width="100%"
                height="100%"
                fit="cover"
                :src="item.url"
                :radius="4"
                round
              />
            </div>
            <!-- 不论是否折叠 - 纯图片展示 -->
            <van-image
              v-else
              width="100%"
              height="100%"
              fit="cover"
              :src="item.url"
              :radius="4"
              round
            />
          </template>
        </SquareContainer>
        <SquareContainer v-if="upload && imagesInFiles.length < imageMax">
          <UploadTrigger icon-size="7vw" @click="onUpload" />
        </SquareContainer>
      </div>
      <div v-if="otherFiles.length > 0">
        <div
          v-for="item in collapsed ? otherFiles.slice(0, 1) : otherFiles"
          :key="item.url || item.original"
          class="mt-20 flex items-center first:mt-16"
          @click="handlePreviewFile(item)"
        >
          <img
            class="h-24 w-21 flex-shrink-0"
            :src="getIconByExt(getExtByName(item.original || item.url))"
          />
          <!-- TODO 文件上传失败可能还要做处理 -->
          <span
            class="mx-7 truncate text-14 leading-20"
            :class="{ 'flex-auto': !collapsed, 'text-red': !item.url }"
            >{{
              (item.original || getFilenameByUrl(item.url)) +
              (!item.url ? '(上传失败)' : '')
            }}</span
          >
          <van-icon
            v-if="removable"
            :size="20"
            :name="icDelete"
            class="flex-shrink-0"
            @click.stop="onDelete(item.idx)"
          />
        </div>
        <div
          v-if="collapsed && otherFiles.length > 1"
          class="mt-12 text-14 leading-20 text-grey-b"
        >
          还有{{
            otherFiles[1].original || getFilenameByUrl(otherFiles[1].url)
          }}等 {{ otherFiles.length - 1 }} 项文件
        </div>
      </div>
      <slot
        v-if="!files || files.length === 0"
        name="empty"
        :handle-upload="onUpload"
      />
      <slot
        v-if="files && files.length > 0"
        name="continue"
        :handle-upload="onUpload"
      >
      </slot>
    </div>
    <FilePreviewer ref="filePreviewer" />
  </div>
</template>

<style scoped>
.file-images-container {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 8px;
  row-gap: 8px;
}
</style>
